#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "tusb.h"

/* System serive includes. */
#include "ServiceRawFlash.h"
#include "ServiceSTMPPartition.h"
#include "ServiceUSBDevice.h"

/* Library includes. */
#include "display.h"
#include "irq.h"
#include "mmu.h"
#include "regsuartdbg.h"

/* Kernel includes. */
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"

/*
NCB		0		BCB


LDLB	512		BCB
BCB		513


DBBT	1024	BCB

BBCR	1032	BCB

REGION	1408	BCB
		1409	BCB
		1410	BCB
		1411	BCB

*/

unsigned char STMP_meta[] = {
    0xFF, 0x00, 0x53, 0x54, 0x4D, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

const unsigned char BCB_meta[] = { //BCB< >
    0xFF, 0xFF, 0x42, 0x43, 0x42, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

const unsigned char ZERO_meta[] = {
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

const unsigned char NCB_s00[] = {

    0x53, 0x54, 0x4D, 0x50, 0x2D, 0x1E, 0x19, 0x06, 0x00, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x43, 0x42, 0x20,
    0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x52, 0x42, 0x49, 0x4E, 0x01, 0x00, 0x00, 0x00, 0xFE, 0x2D, 0x1E, 0x19, 0x06, 0x00, 0x00, 0x00

};

const unsigned char LDLB_S01[] = {
    0x53, 0x54, 0x4D, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4C, 0x44, 0x4C, 0x42,
    0x00, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x04, 0x00, 0x00, 0xC0, 0x04, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0x52, 0x42, 0x49, 0x4C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
    /*
    0x53, 0x54, 0x4D, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4C, 0x44, 0x4C, 0x42, 
    0x00, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x04, 0x00, 0x00, 0xC0, 0x04, 0x00, 0x00, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
    0x52, 0x42, 0x49, 0x4C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF*/

};

const unsigned char DBBT[] = {
    0x53, 0x54, 0x4D, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x42, 0x42, 0x54,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x52, 0x42, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const unsigned char BBCR[] = {
    0x53, 0x54, 0x4D, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x42, 0x42, 0x43, 0x52,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0x52, 0x42, 0x49, 0x42, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

NandConfigBlockInfo_t *p_NandConfigBlock;

NandConfigBlockInfo_t SysNandConfigBlock;

NandConfigBlockRegionInfo_t Regions[10];
unsigned int nRegion = 0;
unsigned char _isRawFlash = true;

unsigned int LDLBBlockPos = 0;

void vScanAndBuildRegionInfo() {
    unsigned int block;
    unsigned int *buffer;

    nRegion = 0;
    buffer = pvPortMalloc(2048);
    //printf("buffer %08x\n",buffer);
    for (block = 0; block < 64; block++) {

        xReadFlashPages(block * 64 + 1, 1, buffer, 5000);

        p_NandConfigBlock = (NandConfigBlockInfo_t *)buffer;

        flush_cache();
        if ((p_NandConfigBlock->iMagicCookie == NAND_CONFIG_BLOCK_MAGIC_COOKIE) &&

            (p_NandConfigBlock->iVersionNum == NAND_CONFIG_BLOCK_VERSION)) {

            memcpy(&SysNandConfigBlock, p_NandConfigBlock, sizeof(SysNandConfigBlock));
            _isRawFlash = false;
            printf("Found %d STMP Region(s).\n", SysNandConfigBlock.iNumRegions);

            for (int i = 0; i < SysNandConfigBlock.iNumRegions; i++) {
                Regions[i].eDriveType = SysNandConfigBlock.Regions[i].eDriveType;
                Regions[i].wTag = SysNandConfigBlock.Regions[i].wTag;
                Regions[i].iNumBlks = SysNandConfigBlock.Regions[i].iNumBlks;
                Regions[i].iChip = SysNandConfigBlock.Regions[i].iChip;
                Regions[i].iStartBlock = SysNandConfigBlock.Regions[i].iStartBlock;
                nRegion++;
                printf("%d: DriveType: %d, Block Size: %d, Start Block: %d, Tag: %08X\n", i,
                       SysNandConfigBlock.Regions[i].eDriveType,
                       SysNandConfigBlock.Regions[i].iNumBlks,
                       SysNandConfigBlock.Regions[i].iStartBlock,
                       SysNandConfigBlock.Regions[i].wTag

                );
            }
            LDLBBlockPos = block;
            printf("LDLBBlockPos=%d\n", LDLBBlockPos);
            break;
        }
    }

    vPortFree(buffer);
}

bool isRawFlash() {
    return _isRawFlash;
}

unsigned int getDataRegonTotalBlocks() {

    if (_isRawFlash) {
        return 0;
    }
    for (int i = 0; i < nRegion; i++) {
        if (Regions[i].eDriveType == kDriveTypeData) {
            return Regions[i].iNumBlks;
        }
    }

    //return 978;
}

unsigned int getDataRegonStartBlock() {

    if (_isRawFlash) {
        return 0;
    }
    for (int i = 0; i < nRegion; i++) {
        if (Regions[i].eDriveType == kDriveTypeData) {
            return Regions[i].iStartBlock;
        }
    }

    //return 30;
}

unsigned int STMPFormatInited = 0;
unsigned int isSTMPDiskInited() {
    return STMPFormatInited;
}

void vSTMPPartition(void *pvParameters) {
    while (!isNANDinited()) {
        vTaskDelay(5);
    }

    vScanAndBuildRegionInfo();
    STMPFormatInited = 1;

    vTaskDelete(NULL);
}